/**
 * @author erond
 */

package master;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Timestamp;
import java.util.Observable;

import javax.swing.event.EventListenerList;
import commonUtility.FtpClientThread;

/**
 * AfterGatewayLinkThread è la classe che genera thread in grado di implementare la parte di comunicazione
 * GATEWAY-GATEWAY (con il nodo SUCCESSIVO che avrà avviato un BeforeGatewayLinkThread)
 */
public class AfterGatewayLink extends Observable implements Runnable {

	private ServerSocket afterGatewayLink_ServerInputSocket = null;
	/**
	 * @return the afterGatewayLink_ServerInputSocket
	 */
	public ServerSocket getAfterGatewayLink_ServerInputSocket() {
		return afterGatewayLink_ServerInputSocket;
	}

	/**
	 * @return the client_serverInputPort
	 */
	public int getClient_serverInputPort() {
		return afterNeighbour_serverInputPort;
	}

	/**
	 * @return the client_serverIP
	 */
	public InetAddress getClient_serverIP() {
		return afterNeighbour_serverIP;
	}

	/**
	 * @return the slaveIP
	 */
	public InetAddress getSlaveIP() {
		return slaveIP;
	}

	/**
	 * @return the slaveDeleteServerPort_after
	 */
	public int getSlaveDeleteServerPort_after() {
		return slaveDeleteServerPort_after;
	}

	private int afterNeighbour_serverInputPort = 0;
	private InetAddress afterNeighbour_serverIP = null;
	private AfterGatewayLink_ServerInput afterGatewayLink_ServerInput = null;
	private AfterGatewayLink_ClientOutput afterGatewayLink_ClientOutput = null;
	private Thread afterGatewayLink_ServerInputThread = null;
	private Thread afterGatewayLink_ClientOutputThread = null;
	private String consoleMessage="";

	/**
	 * @return the afterGatewayLink_ServerInputThread
	 */
	public Thread getAfterGatewayLink_ServerInputThread() {
		return afterGatewayLink_ServerInputThread;
	}

	/**
	 * @return the afterGatewayLink_ClientOutputThread
	 */
	public Thread getAfterGatewayLink_ClientOutputThread() {
		return afterGatewayLink_ClientOutputThread;
	}

	private MasterJobServer masterJobServer = null;
	private InetAddress slaveIP = null;
	private int slaveDeleteServerPort_after = 0;
	public static EventListenerList listenerList = null;
	private MasterMonitor masterMonitor = null;
	private Object semaphore;

	/**
	 * @return the semaphore
	 */
	public Object getSemaphore() {
		return semaphore;
	}

	/**
	 * @param semaphore the semaphore to set
	 */
	public void setSemaphore(Object semaphore) {
		this.semaphore = semaphore;
	}

	public void setMasterJobServer (MasterJobServer m) {
		masterJobServer = m;
	}

	public MasterJobServer getMasterJobServer() {
		return masterJobServer;
	}

	public AfterGatewayLink_ServerInput getAfterGatewayLink_ServerInput() {
		return afterGatewayLink_ServerInput;
	}

	public AfterGatewayLink_ClientOutput getAfterGatewayLink_ClientOutput() {
		return afterGatewayLink_ClientOutput;
	}

	public void setConsoleMessage(String consoleMessage) {
		this.consoleMessage = consoleMessage;
	}

	public String getConsoleMessage() {
		return consoleMessage;
	}


	/**
	 * AfterGatewayLink_ServerInputThread sarà un serverThread in attesa che il vicino AFTER richieda
	 * dei client da usurpare. In quel caso il relativo thread del vicino
	 * gatewayClient_ClientOutput mi invierà il file che per lui è before_thief.txt e per me è after_delete.txt
	 */
	class AfterGatewayLink_ServerInput extends Observable implements Runnable {

		private ServerSocket localAfterGatewayLink_ServerSocket = null;
		private Socket localAfterGatewayLink_ClientSocket = null;
		/**
		 * @return the masterDeleteClient_AfterLinkThread
		 */
		public MasterDeleteClient_AfterLinkThread getMasterDeleteClient_AfterLinkThread() {
			return masterDeleteClient_AfterLinkThread;
		}

		/**
		 * @return the localAfterGatewayLink_ServerSocket
		 */
		public ServerSocket getLocalAfterGatewayLink_ServerSocket() {
			return localAfterGatewayLink_ServerSocket;
		}

		/**
		 * @return the localAfterGatewayLink_ClientSocket
		 */
		public Socket getLocalAfterGatewayLink_ClientSocket() {
			return localAfterGatewayLink_ClientSocket;
		}

		private MasterMonitor masterMonitor = null;
		private String consoleMessage="";
		private MasterDeleteClient_AfterLinkThread masterDeleteClient_AfterLinkThread = null;
		private String lastSignalSent = "";
		private String lastSignalReceived = "";

		/**
		 * @return the lastSignalSent
		 */
		public String getLastSignalSent() {
			return lastSignalSent;
		}

		/**
		 * @return the lastSignalReceived
		 */
		public String getLastSignalReceived() {
			return lastSignalReceived;
		}

		/**
		 * @return the consoleMessage
		 */
		public String getConsoleMessage() {
			return consoleMessage;
		}

		public AfterGatewayLink_ServerInput(ServerSocket serverSocket,MasterMonitor monitor) {
			localAfterGatewayLink_ServerSocket = serverSocket;
			masterMonitor=monitor;
			addObserver(masterMonitor);
		}

		public void run() {

			consoleMessage="AfterGatewayLink_ServerInputThread avviato...";
			setChanged();
			notifyObservers(this);
			System.out.println(consoleMessage);

			try {

				while(true) {

					masterDeleteClient_AfterLinkThread =
						new MasterDeleteClient_AfterLinkThread(slaveIP,slaveDeleteServerPort_after);
					masterDeleteClient_AfterLinkThread.setName("masterDeleteClient_AfterLinkThread");

					try {
						/**
						 * Il Thread,una volta creata la socket si pone in attesa che la connessione
						 * venga accettata anche lato client. Attende inoltre che venga effettuata una richiesta
						 * di servizio. BLOCCANTE!!!
						 */
						localAfterGatewayLink_ClientSocket=localAfterGatewayLink_ServerSocket.accept();
						consoleMessage="AfterGatewayLink_ServerInputThread: sbloccato dalla accept";
						setChanged();
						notifyObservers(this);
						System.out.println(consoleMessage);
					}
					catch (IOException ioe) {
						System.out
						.println("AfterGatewayLink_ServerInputThread: Problemi nella afterGatewayLinkSocket.accept() "
								+ "su socket: ");
						ioe.printStackTrace();
						// il server continua l'esecuzione riprendendo dall'inizio del ciclo
						return;
					}
					catch (Exception e) {
						//generica eccezione
						System.out
						.println("AfterGatewayLink_ServerInputThread: ECCEZIONE nella accept()"
								+ "su socket: ");
						e.printStackTrace();
					}

					DataInputStream inSock = null;
					DataOutputStream outSock = null;

					try {
						// creazione stream di input e out da socket
						inSock = new DataInputStream(localAfterGatewayLink_ClientSocket.getInputStream());
						outSock = new DataOutputStream(localAfterGatewayLink_ClientSocket.getOutputStream());
					}
					catch (IOException ioe) {
						System.out
						.println("AfterGatewayLink_ServerInputThread: Problemi nella creazione degli stream di input/output "
								+ "su socket: ");
						ioe.printStackTrace();
						// il server continua l'esecuzione riprendendo dall'inizio del ciclo
						return;
					}

					/**
					 * Il thread è in attesa che il vicino AFTER (in particolare il thread
					 * GatewayClient_ClientOutputThread) gli invii un file che per lui si chiama
					 * before_thief.txt. Io lo devo rinominare in after_wants_thief.txt ed inviarlo allo slave.
					 */


					consoleMessage=Thread.currentThread().getName()+": Attendo di ricevere BEFORETHIEFREADY dal vicino AFTER...";
					setChanged();
					notifyObservers(this);
					System.out.println(consoleMessage);

					String signal=null;

					try {
						while(inSock.available()>=0) {
							signal=inSock.readUTF();
							if(signal.equalsIgnoreCase("BEFORETHIEFREADY")) {
								lastSignalReceived=signal;
								consoleMessage=Thread.currentThread().getName()+": Ricevuto "+signal+" ..avvio la procedura!";
								System.out.println(consoleMessage);
								setChanged();
								notifyObservers(this);


								/**
								 * se ricevo questa stringa vuol dire che il vicino AFTER ha inviato al mio ftp server
								 * il file contenente i mac address relativi ai job che è in grado (LOAD BALANCING)
								 * di usurparmi. Il file verrà salvato in gatewayTmp come after_wants_thief.txt
								 * CONTROLLO DI SICUREZZA: invio al vicino la stringa BEFORETHIEFREADYRECEIVED
								 */
								outSock.writeUTF("BEFORETHIEFREADYRECEIVED");
								lastSignalSent="BEFORETHIEFREADYRECEIVED";
								setChanged();
								notifyObservers(this);
								break;
							}
						}
					} catch (IOException e) {
						e.printStackTrace();
						System.exit(1);
					}

					try {
						outSock.flush();
					} catch (IOException ioe) {
						System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
						ioe.printStackTrace();
					}

					Timestamp timestamp = new Timestamp(System.currentTimeMillis());
					consoleMessage="["+timestamp+"] AfterGatewayLink_ServerInputThread: ricevuto after_wants_thief.txt dal vicino AFTER";
					setChanged();
					notifyObservers(this);
					System.out.println(consoleMessage);

					/**
					 * Ora dovrei inviare questo after_wants_thief.txt allo Slave, o relegare il lavoro al
					 * MasterDeleteClient_AfterLinkThread.
					 */

					masterDeleteClient_AfterLinkThread.start();
					setChanged();
					notifyObservers(this);

					try {
						//attendo finchè il thread non completa il suo run()
						masterDeleteClient_AfterLinkThread.join();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					/**
					 * Ora so che il MasterDeleteClient_AfterLinkThread ha terminato l'esecuzione,
					 * rendendomi disponibile in locale il file send_after_transfer_job.xml, che io
					 * devo inviare al GatewayClient_ClientOutput del nodo AFTER, il quale riceverà il file
					 * come got_after_transfer_job.xml
					 */

					System.out.println("GatewayClient_ServerInputThread: invio send_after_transfer_job.xml al vicino AFTER");

					FtpClientThread send_after_transfer_job = new FtpClientThread(afterNeighbour_serverIP, 2121, "./gatewayTmp/send_after_transfer_job.xml", "got_after_transfer_job.xml");
					send_after_transfer_job.setName("GatewayClient_ServerInput_ftpClientThread_sendAfterTransferJob");
					send_after_transfer_job.start();

					//Attendo che il trasferimento sia completato
					try {
						send_after_transfer_job.join();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

					while(true) {
						try {
							outSock.writeUTF("AFTERTRANSFERJOBREADY");
							lastSignalSent="AFTERTRANSFERJOBREADY";
							setChanged();
							notifyObservers(this);
							while(inSock.available()>=0) {
								signal=inSock.readUTF();
								if(signal.equalsIgnoreCase("AFTERTRANSFERJOBREADYRECEIVED")) {
									lastSignalReceived=signal;
									consoleMessage=Thread.currentThread().getName()+": Ricevuto "+signal+" ..continuo!";
									setChanged();
									notifyObservers(this);
									System.out.println(consoleMessage);
									/**
									 * se ricevo questa stringa vuol dire che lo slave ha ricevuto AFTERTRANSFERJOBREADY
									 */
									break;
								}
							}
							break;
						} catch (IOException e) {
							e.printStackTrace();
							System.exit(1);
						}
					}

					try {
						outSock.flush();
					} catch (IOException ioe) {
						System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
						ioe.printStackTrace();
					}

					timestamp = new Timestamp(System.currentTimeMillis());
					consoleMessage="["+timestamp+"] AfterGatewayLink_ServerInputThread: inviato send_after_transfer_job.xml al vicino AFTER!";
					setChanged();
					notifyObservers(this);
					System.out.println(consoleMessage);


					consoleMessage="AfterGatewayLink_ServerInputThread: finito il servizio. Torno in attesa di richieste...";
					setChanged();
					notifyObservers(this);
					System.out.println(consoleMessage);					

				}//fine while(true)    

			} catch(Exception e) {
				System.err.println("AfterGatewayLink_ServerInputThread: Errore,chiudo...\n");
				e.printStackTrace();
				try {
					localAfterGatewayLink_ClientSocket.close();
				} catch (IOException e1) {
					e1.printStackTrace();
					System.exit(1);
				}
				System.exit(1);
			}

		}//fine run()

	}//fine AfterGatewayLink_ServerInputThread



	/**
	 * Thread che si occupa di inviare al beforeGatewayLink_ServerInputThread del vicino AFTER il file
	 * after_thief.txt contenente i macaddress dei clienti di cui vuole assumere il servizio.
	 * Alla terminazione il thread avrà ricevuto got_before_transfer_job.xml.
	 * @author erond
	 * AfterGatewayLink_ClientOutputThread sarà un clientThread che dopo aver instaurato la connessione con il
	 * beforeGatewayLink_ServerInputThread del vicino AFTER, si sospenderà in wait() fino a quando il
	 * masterJobServerThread, dopo aver scaricato gli xml proprio e devi vicini, e applicato il protocollo di load balancing,
	 * necessiterà di comunicare al vicino AFTER la lista di client da usurpare. In quel caso scatenerà una notify()
	 * sul AfterGatewayLink_ClientOutputThread, il quale invierà al beforeGatewayLink_ServerInputThread del vicino AFTER
	 * il file after_thief.txt (preparato precedentemente dal MasterJobServerThread).
	 * A quel punto dovrò attendere che il beforeGatewayLink_ServerInputThread del vicino AFTER invii in ritorno
	 * il file che per lui è send_before_transfer_job.xml e per me diventerà got_after_transfer_job.xml che il MasterJobServer,
	 * poichè era in join() su di me, saprà essere pronto per essere inviato allo slave alla mia terminazione.
	 */
	class AfterGatewayLink_ClientOutput extends Observable implements Runnable {

		private Socket localAfterGatewayLink_ClientSocket = null;
		/**
		 * @return the localAfterGatewayLink_ClientSocket
		 */
		public Socket getLocalAfterGatewayLink_ClientSocket() {
			return localAfterGatewayLink_ClientSocket;
		}

		private DataInputStream inSock = null;
		private DataOutputStream outSock = null;
		private MasterJobServer masterToNotify = null;
		private MasterMonitor masterMonitor = null;

		public AfterGatewayLink_ClientOutput(MasterMonitor monitor) {
			masterMonitor=monitor;
			addObserver(masterMonitor);
		}

		public void setMasterJobServerThread (MasterJobServer m) {
			masterToNotify = m;
		}

		private String lastSignalSent="";
		/**
		 * @return the lastSignalSent
		 */
		public String getLastSignalSent() {
			return lastSignalSent;
		}

		private String consoleMessage="";

		/**
		 * @return the consoleMessage
		 */
		public String getConsoleMessage() {
			return consoleMessage;
		}

		/**
		 * @return the lastSignalReceived
		 */
		public String getLastSignalReceived() {
			return lastSignalReceived;
		}

		private String lastSignalReceived="";

		public void run() {

			while(true) {

				consoleMessage="AfterGatewayLink_ClientOutputThread: Inizio ciclo...";
				setChanged();
				notifyObservers(this);
				System.out.println(consoleMessage);

				synchronized (this) {
					/**
					 * mi pongo subito in attesa di essere risvegliato. Al risveglio deve essere pronto
					 * all'invio al gateway vicino (SUCCESSIVO, quindi lo riceverà il thread
					 * GatewayClient_ServerInputThread) del file after_thief.txt.
					 */
					try {
						this.wait();
						consoleMessage="AfterGatewayLink_ClientOutputThread: SBLOCCATO dal masterJobServerThread!";
						setChanged();
						notifyObservers(this);
						System.out.println(consoleMessage);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}

				/**
				 * questo costruttore dovrà avere i parametri del gateway after
				 */
				try {
					localAfterGatewayLink_ClientSocket = new Socket(afterNeighbour_serverIP,afterNeighbour_serverInputPort);
					consoleMessage="AfterGatewayLink_ClientOutputThread: ho creato la afterGatewayLink_ClientOutputSocket";
					setChanged();
					notifyObservers(this);
					System.out.println(consoleMessage);
					if (localAfterGatewayLink_ClientSocket.isConnected()) System.out.println("AfterGatewayLink_ClientOutput SOCKET CONNECTED!!");
				} catch (IOException e) {
					e.printStackTrace();
				}

				try {
					// creazione stream di input e out da socket
					inSock = new DataInputStream(localAfterGatewayLink_ClientSocket.getInputStream());
					outSock = new DataOutputStream(localAfterGatewayLink_ClientSocket.getOutputStream());
				}
				catch (IOException ioe) {
					System.out
					.println("AfterGatewayLink_ClientOutputThread: Problemi nella creazione degli stream di input/output "
							+ "su socket: "+localAfterGatewayLink_ClientSocket);
					ioe.printStackTrace();
					// il server continua l'esecuzione riprendendo dall'inizio del ciclo
					return;
				}

				/**
				 * Devo inviare il file after_thief.txt all'ftpserver sul master del gatewayClient_ServerInput del gateway
				 * dopo di me. Lui lo riceverà e rinominerà in before_wants_thief.txt
				 */

				FtpClientThread after_thief = new FtpClientThread(afterNeighbour_serverIP, 2121, "./gatewayTmp/after_thief.txt", "before_wants_thief.txt");
				after_thief.setName("AfterGatewayLink_ClientOutput_ftpClientThread_afterThief");
				after_thief.start();

				//Attendo che il trasferimento sia completato
				try {
					after_thief.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				consoleMessage="AfterGatewayLink_ClientOutputThread: inviato after_thief.txt al gatewayClient_ServerInputThread_AFTER";
				setChanged();
				notifyObservers(this);
				System.out.println(consoleMessage);

				String signal = null;

				while(true) {
					try {
						outSock.writeUTF("AFTERTHIEFREADY");
						lastSignalSent="AFTERTHIEFREADY";
						setChanged();
						notifyObservers(this);
						while(inSock.available()>=0) {
							signal=inSock.readUTF();
							if(signal.equalsIgnoreCase("AFTERTHIEFREADYRECEIVED")) {
								lastSignalReceived=signal;
								consoleMessage="AfterGatewayLink_ClientOutputThread: Ricevuto "+signal+" ..continuo!";
								setChanged();
								notifyObservers(this);
								System.out.println(consoleMessage);
								/**
								 * se ricevo questa stringa vuol dire che lo slave ha ricevuto AFTERTHIEFREADY
								 */
								break;
							}
						}
						break;
					} catch (IOException e) {
						e.printStackTrace();
						System.exit(1);
					}
				}

				try {
					outSock.flush();
				} catch (IOException ioe) {
					System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
					ioe.printStackTrace();
				}

				consoleMessage="AfterGatewayLink_ClientOutputThread: ho inviato after_thief.txt al vicino AFTER";
				setChanged();
				notifyObservers(this);
				System.out.println(consoleMessage);

				/**
				 * Ora il thread dovrà attendere che gli venga inviato il file che per lui sarà
				 * got_before_transfer_job.xml (e che per il sender era send_before_transfer_job.xml)
				 */

				consoleMessage="AfterGatewayLink_ClientOutputThread: Attendo di ricevere BEFORETRANSFERJOBREADY dal vicino AFTER...";
				setChanged();
				notifyObservers(this);
				System.out.println(consoleMessage);

				try {
					while(inSock.available()>=0) {
						signal=inSock.readUTF();
						if(signal.equalsIgnoreCase("BEFORETRANSFERJOBREADY")) {
							lastSignalReceived=signal;
							consoleMessage="AfterGatewayLink_ClientOutputThread: Ricevuto "+signal+" ..avvio la procedura!";
							setChanged();
							notifyObservers(this);
							System.out.println(consoleMessage);

							/**
							 * se ricevo questa stringa vuol dire che il vicino AFTER ha inviato al mio ftp server
							 * il file contenente i mac address relativi ai job che gli ho usurpato
							 * di usurparmi.
							 * CONTROLLO DI SICUREZZA: invio al vicino la stringa BEFORETRANSFERJOBREADYRECEIVED
							 */
							outSock.writeUTF("BEFORETRANSFERJOBREADYRECEIVED");
							lastSignalSent="BEFORETRANSFERJOBREADYRECEIVED";
							setChanged();
							notifyObservers(this);
							break;
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
					System.exit(1);
				}

				try {
					outSock.flush();
				} catch (IOException ioe) {
					System.err.println(Thread.currentThread().getName()+": Eccezione nella flush");
					ioe.printStackTrace();
				}


				consoleMessage="AfterGatewayLink_ClientOutputThread: ricevuto got_before_transfer_job.xml dal vicino AFTER";
				setChanged();
				notifyObservers(this);
				System.out.println(consoleMessage);

				/**
				 * Sblocco il masterJobServerThread, notificandogli così che il file 
				 * got_before_transfer_job.xml è disponibile.
				 * Poi ritorno in attesa all'inizio dell'esecuzione del thread.
				 */

				consoleMessage="AfterGatewayLink_ClientOutputThread: imposto a false l' afterFlag attualmente con valore "+masterToNotify.getAfterFlag();
				System.out.println(consoleMessage);
				setChanged();
				notifyObservers(this);

				masterToNotify.setAfterFlag(false);

				synchronized (semaphore) {
					semaphore.notify();
				}

				consoleMessage="AfterGatewayLink_ClientOutputThread: ora afterFlag è "+masterToNotify.getAfterFlag();
				System.out.println(consoleMessage);
				setChanged();
				notifyObservers(this);

				try {
					consoleMessage="AfterGatewayLink_ClientOutputThread: Termino e chiudo la socket threadOutputClientSocket";
					inSock.close();
					outSock.close();
					localAfterGatewayLink_ClientSocket.close();
					System.out.println(consoleMessage);
					setChanged();
					notifyObservers(this);
				} catch (IOException se) {
					se.printStackTrace();
				}

			}//fine while(true)

		}//fine run()

	}//fine AfterGatewayLinkOutputThread
	
	
	/**
	 * @param serverInputSocket Server socket in ascolto di richieste da parte dell' After gateway
	 * @param afterNeighbour_ServerInputPort Porta del ServerInput avviato sull' after gateway vicino
	 * @param afterNeighbour_ServerIP Indirizzo IP del ServerInput avviato sulll'after gateway vicino
	 * @param slaveIP Indirizzo IP locale dello slave
	 * @param slaveDeleteServerPort_after Porta sulla quale trovare il servizio di Cancellazione client sullo slave
	 * @param masterMonitor monitor di sistema Observer sul quale scatenare eventi notifyObserver
	 */
	public AfterGatewayLink(ServerSocket serverInputSocket, int afterNeighbour_ServerInputPort,
			InetAddress afterNeighbour_ServerIP, InetAddress slaveIP, int slaveDeleteServerPort_after,MasterMonitor masterMonitor) {
		this.afterGatewayLink_ServerInputSocket = serverInputSocket;
		this.afterNeighbour_serverInputPort = afterNeighbour_ServerInputPort;
		this.afterNeighbour_serverIP = afterNeighbour_ServerIP;
		this.slaveIP = slaveIP;
		this.slaveDeleteServerPort_after = slaveDeleteServerPort_after;
		this.masterMonitor=masterMonitor;
		this.addObserver(this.masterMonitor);
	}	

	//metodo per lanciare il thread
	public void run() {

		consoleMessage="AfterGatewayLinkThread avviato...creo i Thread di input e output";

		afterGatewayLink_ServerInput = new AfterGatewayLink_ServerInput(afterGatewayLink_ServerInputSocket,masterMonitor);
		afterGatewayLink_ServerInputThread = new Thread(afterGatewayLink_ServerInput);
		afterGatewayLink_ServerInputThread.setName("afterGatewayLink_ServerInputThread");

		afterGatewayLink_ClientOutput = new AfterGatewayLink_ClientOutput(masterMonitor);
		afterGatewayLink_ClientOutputThread = new Thread(afterGatewayLink_ClientOutput);
		afterGatewayLink_ClientOutputThread.setName("afterGatewayLink_ClientOutputThread");

		afterGatewayLink_ServerInputThread.start();
		afterGatewayLink_ClientOutputThread.start();

		setChanged();
		notifyObservers(this);

		//creati i demoni, attendo la loro terminazione (che non dovrebbe avvenire) prima di terminare
		try {
			afterGatewayLink_ServerInputThread.join();
		} catch (InterruptedException e) {
			System.out.println("\nAfterGatewayLinkThread: eccezione nel join col thread Input...");
			e.printStackTrace();
		}
		try {
			afterGatewayLink_ClientOutputThread.join();
		} catch (InterruptedException e) {
			System.out.println("\nAfterGatewayLinkThread: eccezione nel join col thread Output...");
			e.printStackTrace();
		}

		consoleMessage="\nAfterGatewayLinkThread: termino!";
		System.out.println(consoleMessage);
		setChanged();
		notifyObservers(this);


	}//fine run()


}//fine AfterGatewayLinkThread
